from wormbane.util.format import (add_indefinite_article, to_count_article,
                                  plural_noun)
from wormbane.util.iterator import flatten

class Thing(object):
    """A game object.

    Things use the default implementations of __eq__() and __nonzero__(); they
    use object identity for comparisons and always test as true in boolean
    contexts. Among other things (no pun intended), this simplifies testing for
    None.
    """

    is_altar = False
    is_armor = False
    is_boulder = False
    is_cell = False
    is_coin = False
    is_corridor = False
    is_corpse = False
    is_door = False
    is_doorway = False
    is_feature = False
    is_food = False
    is_floor = False
    is_hole = False
    is_item = False
    is_level = False
    is_monster = False
    is_mushroom = False
    is_pit = False
    is_rock = False
    is_stair = False
    is_wall = False
    is_weapon = False

    kind = 'thing'
    env = None
    color = None
    material = None
    name = None
    count = 1
    weight = None
    mentioned = False
    desc_attrs = 'kind'

    def __init__(self, **kwargs):
        for name, value in kwargs.iteritems():
            setattr(self, name, value)
        self.inv = []

    @property
    def singular(self):
        return self.count == 1

    @property
    def plural(self):
        return self.count != 1

    @property
    def singular_desc(self):
        if self.name:
            return self.name
        parts = (getattr(self, attr) for attr in flatten(self.desc_attrs))
        desc = ' '.join(part for part in flatten(parts) if part)
        return add_indefinite_article(desc)

    @property
    def plural_desc(self):
        return to_count_article(self.count, plural_noun(self.singular_desc))

    @property
    def desc(self):
        return self.singular_desc if self.singular else self.plural_desc

    def __str__(self):
        return self.desc

    @property
    def load(self):
        return sum(thing.total_weight for thing in self.inv)

    @property
    def total_weight(self):
        return self.weight * self.count + self.load

    def add_inv(self, thing):
        self.inv.append(thing)

    def remove_inv(self, thing):
        self.inv.remove(thing)

    def move_to(self, env):
        if self.env:
            self.env.remove_inv(self)
        self.env = env
        if self.env:
            self.env.add_inv(self)

    def move_inv_to(self, env):
        while self.inv:
            self.inv[-1].move_to(env)

    @property
    def deep_env(self):
        thing = self.env
        while thing:
            yield thing
            thing = thing.env

    @property
    def monster_env(self):
        for thing in self.deep_env:
            if thing.is_monster:
                return thing
        return None

    @property
    def cell_env(self):
        for thing in self.deep_env:
            if thing.is_cell:
                return thing
        return None

    @property
    def level_env(self):
        for thing in self.deep_env:
            if thing.is_level:
                return thing
        return None

    @property
    def pos(self):
        cell = self.cell_env
        return cell.pos if cell else None

class MoveError(Exception):
    pass
